#ifndef PRYN_GUI_EVENT_H
#define PRYN_GUI_EVENT_H

#include <pryn/platform.h>

typedef struct PrynGuiEvent PrynGuiEvent; /// A Gui event.
typedef enum PrynGuiEventType PrynGuiEventType;
typedef struct PrynGuiReceiver PrynGuiReceiver;

/// Double-linked list of PrynGuiReceiver.
typedef PrynDList (PrynGuiReceiver) PrynGuiReceiverList;

#include <pryn/gui/control.h>
#include <pryn/gui/painter.h>

/** @defgroup PrynGuiReceiver PrynGuiReceiver C API
	@{ */

PrynCallbackTypedef (PrynResult, PrynGuiReceiverFunction, (PrynGuiEvent *event, void *data)); ///< Function to receive an event.
PrynCallbackTypedef (void, PrynGuiReceiverDestroyFunction, (PrynState *state, void *data)); ///< Optional function to destroy the data parameter.

PrynGuiImport (PrynResult, prynGuiEventCreate, (PrynGuiEvent **event, PrynGuiEventType type, PrynGuiControl *control)) ///< Create a new event object. This is in case you need to synthesize an event.
PrynGuiImport (PrynResult, prynGuiEventClear, (PrynGuiEvent *event, PrynGuiEventType type, PrynGuiControl *control)) ///< Clear the parameters of the event so that it becomes an event for this type.

PrynGuiImport (PrynResult, prynGuiEventDestroy, (PrynGuiEvent *event)) ///< Free all data associated with the event, including the event pointer. The pointer is unusable after this is called.
PrynGuiImport (PrynResult, prynGuiEventEmpty, (PrynGuiEvent *event)) ///< Free all data associated with the event, but not the event object itself. 

PrynGuiImport (PrynResult, prynGuiEventDispatch, (PrynGuiEvent *event)) ///< Dispatch the event to all matching receivers in the field.

/** @name Properties
@{ */

PrynGuiImport (PrynResult, prynGuiEventTypeMatch, (PrynGuiEventType type, PrynGuiEventType match)) ///< Return whether the event type matches the type or mask in match. For example, this will match a specific event in type to #PrynGuiEventTypeAll in match.
PrynGuiImport (PrynResult, prynGuiEventTypeAbstract, (PrynGuiEventType type, PrynGuiEventType *abstract)) ///< Return the general form of the event type. For example, #PrynGuiEventTypeMouseLeftButtonDown will return #PrynGuiEventTypeMouseLeftButton, then #PrynGuiEventTypeMouse if called again.

PrynGuiImport (PrynResult, prynGuiEventState, (PrynGuiEvent *event, PrynState **output)) ///< State for the event or 0 if there's a failure.
PrynGuiImport (PrynResult, prynGuiEventType, (PrynGuiEvent *event, PrynGuiEventType *output)) ///< Type of the event or #PrynGuiEventTypeNone if there's a failure.
PrynGuiImport (PrynResult, prynGuiEventControl, (PrynGuiEvent *event, PrynGuiControl **output)) ///< Control this event is related to or 0 if there's a failure.

/** @} */ // (Properties group)

/** @name Painter Functions
These functions are only valid for #PrynGuiEventTypePaint events.
@{ */

PrynGuiImport (PrynResult, prynGuiEventPainter, (PrynGuiEvent *event, PrynGuiPainter **output)) ///< Painter attached to the event; this is valid for #PrynGuiEventTypePaint events only. Otherwise 0 is returned, or if there's a failure.

PrynGuiImport (PrynResult, prynGuiEventSetPainter, (PrynGuiEvent *event, PrynGuiPainter *painter)) ///< Set the painter for a #PrynGuiEventTypePaint event; attempting to set it on anything else results in an error.

/** @} */ // (Painter group)

/** @name Mouse Functions
These functions are only valid for #PrynGuiEventTypeMouse class events.
@{ */

PrynGuiImport (PrynResult, prynGuiEventMousePosition, (PrynGuiEvent *event, int *x, int *y)) ///< Retrieve the position of the mouse in the client area of the control when the event was generated. This is only valid for #PrynGuiEventTypeMouse class events; otherwise 0 is written to x and y.
PrynGuiImport (PrynResult, prynGuiEventMouseLeftButton, (PrynGuiEvent *event)) ///< State of the left mouse button when the event was generated. This is only valid for #PrynGuiEventTypeMouse class events; otherwise false is returned.
PrynGuiImport (PrynResult, prynGuiEventMouseMiddleButton, (PrynGuiEvent *event)) ///< State of the middle mouse button when the event was generated. This is only valid for #PrynGuiEventTypeMouse class events; otherwise false is returned.
PrynGuiImport (PrynResult, prynGuiEventMouseRightButton, (PrynGuiEvent *event)) ///< State of the right mouse button when the event was generated. This is only valid for #PrynGuiEventTypeMouse class events; otherwise false is returned.

/** @} */ // (Mouse Functions group)

#ifdef PrynGuiInternalStructs
/// A receiver for an event.
struct PrynGuiReceiver
{
#ifdef PrynGuiInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define #PrynGuiInternal.
	@{ */

	PrynGuiReceiver *pNext; ///< Next receiver in the list or 0 if this is last.
	PrynGuiReceiver *pPrevious; ///< Previous receiver in the list or 0 if this is first.
	
	PrynGuiEventType pType; ///< Type of events to receive.
	void *pData; ///< Data parameter passed to the function.
	PrynGuiReceiverFunction pFunction; ///< Function to receive the event.
	PrynGuiReceiverDestroyFunction pDestroyFunction; ///< Optional function to destroy the data when the receiver is removed.
#endif /* PrynGuiInternal */

#ifdef __cplusplus
#endif /* __cplusplus */
};
#endif /* PrynGuiInternalStructs */

/** @} */

/** @defgroup PrynGuiEvent PrynGuiEvent C API
	@{ */

/// The type of an event. There are pseudo-events which are used by receivers to match multiple event types; these are indicated with a (class) note at the beginning of their documentation.
enum PrynGuiEventType
{
	PrynGuiEventTypeNone = 0, ///< An invalid value; this is not an event type.
	PrynGuiEventTypeAll = 1, ///< (class) This is used by receivers to indicate that it should receive all events of all types.
	
	PrynGuiEventTypePaint = 2, ///< A region of the control needs to be painted. The painter parameter is valid.
	PrynGuiEventTypeDestroy = 16, ///< The control is being destroyed.
	
	PrynGuiEventTypeMouse = 3, ///< (class) Any mouse event. prynGuiEventMousePosition is the position of the mouse on the client area of the control. prynGuiEventMouseLeftButton, prynGuiEventMouseRightButton, and prynGuiEventMouseMiddleButton reflect the button state of the mouse when the event was generated. prynGuiEventKeyControl and prynGuiEventKeyShift reflect the key state when the event was generated.
	PrynGuiEventTypeMouseMove = 4, ///< The mouse has moved while over top of the control.
	PrynGuiEventTypeMouseButton = 5, ///< (class) Any button has been released or pressed.
	PrynGuiEventTypeMouseLeftButton = 6, ///< (class) The left mouse button has been released or pressed.
	PrynGuiEventTypeMouseLeftButtonDown = 7, ///< The left mouse button has been pressed.
	PrynGuiEventTypeMouseLeftButtonUp = 8, ///< The left mouse button has been released.
	PrynGuiEventTypeMouseMiddleButton = 9, ///< (class) The middle mouse button has been released or pressed.
	PrynGuiEventTypeMouseMiddleButtonDown = 10, ///< The middle mouse button has been pressed.
	PrynGuiEventTypeMouseMiddleButtonUp = 11, ///< The middle mouse button has been released.
	PrynGuiEventTypeMouseRightButton = 12, ///< (class) The right mouse button has been released or pressed.
	PrynGuiEventTypeMouseRightButtonDown = 13, ///< The right mouse button has been pressed.
	PrynGuiEventTypeMouseRightButtonUp = 14, ///< The right mouse button has been released.
	// next value: 17
};

#ifdef PrynGuiInternalStructs
/// A Gui event.
struct PrynGuiEvent
{
#ifdef PrynGuiInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynGuiInternal.
	@{ */

	PrynGuiEventType pType; ///< Type of the event.
	PrynGuiControl *pControl; ///< Control this event is related to.
	PrynGuiPainter *pPainter; ///< Painter for PrynGuiEventTypePaint events.
	bool pReceived; ///< Whether any receivers matched the event, even if it returned an error.
	int pMouseX; ///< Horizontal position of the mouse.
	int pMouseY; ///< Vertical position of the mouse.
	bool pMouseLeftButton; ///< Left mouse button state.
	bool pMouseMiddleButton; ///< Middle mouse button state.
	bool pMouseRightButton; ///< Right mouse button state.
	bool pKeyControl, pKeyShift; ///< Key states.

/** @} */
#endif /* PrynGuiInternal */

#ifdef __cplusplus
	static inline PrynGuiEvent *Create (PrynGuiEventType type, PrynGuiControl *control); ///< Create a new event object. This is in case you need to synthesize an event. Be certain to #destroy the error after you're done with it.

	static inline bool MatchType (PrynGuiEventType type, PrynGuiEventType match) { return PrynIsTrue (prynCheckErrorNull (prynGuiEventTypeMatch (type, match))); } ///< Return whether the event type matches the type or mask in match. For example, this will match a specific event type to #PrynGuiEventTypeAll in match.
	static inline PrynGuiEventType AbstractType (PrynGuiEventType type) { PrynGuiEventType result; prynCheckErrorNull (prynGuiEventTypeAbstract (type, &result)); return result; } ////< Return the general form of the event type. For example, #PrynGuiEventTypeMouseLeftButtonDown will return #PrynGuiEventTypeMouseLeftButton, then #PrynGuiEventTypeMouse if called again.

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

	inline void clear (PrynGuiEventType type, PrynGuiControl *control); ///< Clear the parameters of the event so that it becomes an event for this type.

	inline void destroy () { prynGuiEventDestroy (this); } ///< Free all data associated with the event, including the event pointer. The pointer is unusable after this is called.

	inline void empty () { checkError (prynGuiEventEmpty (this)); } ///< Free all data associated with the event, but not the event object itself. 

	inline bool match (PrynGuiEventType match) { return PrynIsTrue (checkError (prynGuiEventTypeMatch (type (), match))); } ///< Return whether the event type matches the type or mask in match. For example, this will match a specific event type to #PrynGuiEventTypeAll in match.

	inline void dispatch () { checkError (prynGuiEventDispatch (this)); } ///< Dispatch the event.

/** @name Properties
@{ */

	inline PrynState *state () { PrynState *result; prynCheckErrorNull (prynGuiEventState (this, &result)); return result; } ///< State for the event.
	inline PrynGuiEventType type () { PrynGuiEventType result; checkError (prynGuiEventType (this, &result)); return result; } ///< Type of the event.
	inline PrynGuiEventType abstractType () { PrynGuiEventType result; checkError (prynGuiEventTypeAbstract (type (), &result)); return result; } ///< Return the general form of the event type. For example, #PrynGuiEventTypeMouseLeftButtonDown will return #PrynGuiEventTypeMouseLeftButton, then #PrynGuiEventTypeMouse if called again with #AbstractType.
	inline PrynGuiControl *control () { PrynGuiControl *result; checkError (prynGuiEventControl (this, &result)); return result; } ///< Control this event is related to.

/** @} */

/** @name Painter Functions
These functions are only valid for #PrynGuiEventTypePaint events.
@{ */

	inline PrynGuiPainter *painter () { PrynGuiPainter *result; checkError (prynGuiEventPainter (this, &result)); return result; } ///< Painter attached to the event.
	inline void painter (PrynGuiPainter *value) { checkError (prynGuiEventSetPainter (this, value)); } ///< Set the painter.

/** @} */

/** @name Mouse Functions
These functions are only valid for #PrynGuiEventTypeMouse class events. 
@{ */

	inline void mousePosition (int *x, int *y) { checkError (prynGuiEventMousePosition (this, x, y)); } ///< Position of the mouse in the client area of the control when the event was generated.
	inline bool mouseLeftButton () { return PrynIsTrue (checkError (prynGuiEventMouseLeftButton (this))); } ///< State of the left mouse button when the event was generated.
	inline bool mouseMiddleButton () { return PrynIsTrue (checkError (prynGuiEventMouseMiddleButton (this))); } ///< State of the middle mouse button when the event was generated.
	inline bool mouseRightButton () { return PrynIsTrue (checkError (prynGuiEventMouseRightButton (this))); } ///< State of the right mouse button when the event was generated.

/** @} */

#endif /* __cplusplus */
};
#endif /* PrynGuiInternalStructs */

/** @} */

#endif /* PRYN_GUI_EVENT_H  */
